[Go] Slice(切片)
前言
Slice是在一個陣列中的一個區段,與陣列一樣,slice 可透過索引的方式存取,同時也具有長度。但與陣列不同的是,**slice 長度是可以改變的**,若只想處理陣列中某片區域可以使用slice。
slice在中括號[]之間沒有表示長度的數字
slice底層實際上還是個陣列- 參考的預設值都是
nil- slice 唯一可以用
==比較的對象為nil,儲存slice參考的變數也無法進行==比較
Slice 基礎用法
宣告 slice
slice宣告方法有兩種:
- 像array一樣宣告, 不須指定 slice 大小
- 使用內建函數make
像 array 一樣宣告
| 1 |  | 
使用內建函數 make
除了上述方法外, Golang 提供了一個名為 make() 的函數來建立 slice。 它分配 長度與給定容量相同的底層陣列,並返回參考該陣列的 slice。
語法結構:
| 1 |  | 
參數說明:
- T: 該 slice 存放的資料類型
- len: 該 slice 的長度
- cap: 該 slice 的容量,此參數是- 非必要的。若省略,則預設為指定的長度
範例:
有指定 slice 的容量:
| 1 |  | 
沒有指定 slice 的容量:
| 1 |  | 
Slice 的零值
slice 的零值為 nil。帶有零值的 slice 沒有任何底層陣列,且長度和容量為 0
| 1 |  | 
常見用法
re-slicing 重新切片
使用冒號間隔兩個參數,此方式可擷取 slice 特定範圍的值。
語法結構
| 1 |  | 
說明
- start: 起始位置,預設值為- 0
- end: 終點,預設值為該 slice 的長度(- len(slice))
需要注意的是:
- [start:end]取出的範圍是- start到- end - 1的範圍
- start,- end值可不給,如:- [:]
| 1 |  | 
重新切片後的 slice 底層陣列不變,僅改變指標位址
觀察到上述重新切片後的 slice 底層陣列不變的情況後,若不想兩個 slice 共用同一個陣列,則要使用另一個內建函數 copy()
拷貝: copy()
copy() 將一個 slice 的內容,複製至另一個 slice (複製 slice 中原始指向的底層陣列至另一個新的 slice ,且新slice 指向新陣列
語法結構
| 1 |  | 
結合 re-slicing 指定要複製的位置
| 1 |  | 
注意以下情況
- len(src-slice)<- len(dst-slice)時: 會覆蓋- dst-slice的前- len(src-slice)個數- 1 
 2
 3
 4
 5
 6- x := []int{1, 2, 3, 4, 5}
 y := []int{6, 7, 8}
 copy(x, y) // 把y複製進x
 fmt.Println("x:", x, "y: ", y) // x: [6 7 8 4 5] y: [6 7 8]
 y[2] = 0
 fmt.Println("x:", x, "y: ", y) // [1 2]
- len(src-slice)>- len(dst-slice)時: 複製- src-slice與- dst-slice等長的值- 1 
 2
 3
 4
 5
 6- x := []int{1, 2, 3, 4, 5}
 y := []int{6, 7, 8}
 copy(y, x) // 把 x 複製進 y
 fmt.Println("x:", x, "y: ", y) // x: [1 2 3 4 5] y: [1 2 3]
 y[2] = 0
 fmt.Println("x:", x, "y: ", y) // x: [1 2 3 4 5] y: [1 2 0]
擴增: append()
在給定的 slice 尾端擴增的新元素,回傳一個新的 slice
若給定的 Slice 無足夠的容量容納這些新元素,則會新配發具有更大容量的底層陣列。原有的 slice 底層陣列中所有元素都會被複製到新陣列內,再添加新元素。
反之,若給定的 slice 有足夠容量來容納新元素,則使用其底層陣列並將新元素附加到同一陣列中。
語法結構
| 1 |  | 
範例
擴增單一元素
| 1 |  | 
ellipsis: 將一個 Slice 添加到另一個 slice
使用運算子: ... 將一個 slice 直接添加至另一 slice
| 1 |  | 
slice 中放 slice
我們亦可於 slice 中放另外一個 slice,類似二維的概念
| 1 |  | 
slice literals
可用與迭代陣列相同的方式對 slice 做迭代。
- for-loop 的形式 (without range)1 
 2
 3for i := 0; i < len(numb); i++ {
 fmt.Println(numb[i])
 }
- for-loop 的形式 (with range)1 
 2
 3for i, v := range numb {
 fmt.Println(i, v)
 }
需注意之處
slice 底層有個指標指向一個 array
slice 本身是一個引用型別,底層會有個指標指向一個array
slice 的長度 == slice 中元素的數量
slice 的長度是 slice 中元素的數量
slice 為 nil 時,不會於 loop 中的 range 執行
| 1 |  | 
上述範例會發現宣告 games 為 slice 型別後,初始值為 nil ,嘗試用迴圈讀取裡面的值,發現沒有印出任何東西,程式仍能夠正常運作。
空的 Slice 值等同於 nil,來看下述範例:
| 1 |  | 
執行的結果為 true,表兩者同等。
array v.s slice 差異
array
- 固定長度,長度在編譯時期(complie time)已經建立
- array 長度屬於型別的一部分
- 宣告後無法改變陣列長度
- 宣告 array 內的元素型別為 int且未給值時,array 內的元素值為0
- array 在長度相同時可進行比較
- array 在長度相同時可進行賦值(assign:=)操作
slice
- 長度可變動
- 長度不屬於型別的一部分
- 長度於執行期(runtime)才變動
- 宣告 slice 而未給值時,slice 內的值為 nil
- slice 只可對 nil值做比較(比較的對象是nil)
- slice 在元素型別相同時即可進行賦值(assign:=)操作,跟長度無關
範例
| 1 |  |